package controllers;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import models.Casa;
import models.Eleitor;
import models.Eleitor.Status;
import models.Materia;
import models.Politico;
import models.Posicao;

import org.apache.commons.codec.binary.Base64;
import org.htmlcleaner.XPatherException;

import play.modules.router.Get;
import play.mvc.Controller;
import votenaweb.VoteNaWebParser;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.restfb.DefaultFacebookClient;
import com.restfb.FacebookClient;
import com.restfb.types.User;

public class Application extends Controller {
	private static final int MAX_AGE = 3600;
	private static final DateFormat FORMATADOR_PARA = new SimpleDateFormat("dd/MM/yyyy");
	private static final String MALE = "male";

	private static final Base64 DECODER = new Base64(true);

	private static final SecretKey HMAC_KEY = new SecretKeySpec("690e62eaf2269deb6b6bee3b34e5e6db".getBytes(),
			"HMACSHA256");

	private static Mac MAC;

	static {
		try {
			MAC = Mac.getInstance("HMACSHA256");
			MAC.init(HMAC_KEY);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		}
	}

	@Get("/profile2")
	public static void profile2() {
		render();
	}

	public static void profile(Eleitor eleitor) {
		if (eleitor.getId() == null) {
			eleitor = Eleitor.find("byFacebookID", eleitor.getFacebookID()).first();
		} else {
			eleitor.setStatus(Status.CONFIGUROU_POLITICOS);
			eleitor.save();
		}

		List<Materia> materias = getMateriasPoliticosAcompanhados(eleitor);

		// FacebookClient facebookClient = new
		// DefaultFacebookClient(eleitor.getToken());
		// List<Long> facebookIDs =
		// Eleitor.find("select distinct e.facebookID from Eleitor e").fetch();
		// String ids = facebookIDs.toString();
		// ids = ids.replaceFirst("\\[", "");
		// ids = ids.replaceAll("\\]", "");

		render(eleitor, materias);
	}

	// TODO imoplementar getMateriasPoliticosAcompanhados apos alex mandar
	// materias e politicos
	private static List<Materia> getMateriasPoliticosAcompanhados(Eleitor eleitor) {
		Set<Politico> politicosAcompanhados = eleitor.getPoliticos();
		List<Materia> materias = Materia.find("select p.materia from Posicao p where p.politico in :politicos")
				.bind("politicos", politicosAcompanhados).fetch();

		return materias;
	}

	@Get("/paginaPolitico")
	public static void paginaPolitico(long idPolitico, long idEleitor) {
		Politico politico = Politico.findById(idPolitico);
		Eleitor eleitor = Eleitor.findById(idEleitor);
		render(politico, eleitor);
	}

	// public static void indexWithSecureSocial() {
	// try {
	// SocialUser user = SecureSocial.getCurrentUser();
	// FacebookClient facebookClient = new DefaultFacebookClient(
	// user.accessToken);
	// User u = facebookClient.fetchObject(user.id.id, User.class);
	// System.out.println(u.getName() + " - " + u.getLocation().getName());
	// Connection<User> myFriends = facebookClient.fetchConnection(
	// user.id.id + "/friends", User.class);
	//
	// int count = 0;
	// for (List<User> myFriendsPage : myFriends) {
	// for (User friend : myFriendsPage) {
	// ++count;
	// System.out.println(count + ": " + friend.getName() + " - " +
	// friend.getLocation());
	// }
	// }
	//
	// Eleitor eleitor = Eleitor.find("byFacebookID", user.id.id).first();
	// if (eleitor != null) {
	// if (eleitor.isConfigurado()) {
	// eleitor.setToken(user.token);
	// eleitor.save();
	// List<Materia> materias = Materia.findAll();
	// render("Application/profile.html", eleitor, materias);
	// } else {
	// render("Application/configuraLocalizacao.html", eleitor);
	// }
	// } else {
	// eleitor = inicializaEleitor(user.id.id, user.accessToken);
	// render("Application/configuraLocalizacao.html", eleitor);
	// }
	// render(user);
	// } catch (Exception e) {
	// e.printStackTrace();
	// }
	// }

	public static void index(String signed_request) {
		try {
			if (signed_request != null) {
				String[] info = signed_request.split("\\.", 2);
				String assinatura = info[0];
				String data = info[1];
				JsonObject objeto = (JsonObject) new JsonParser().parse(new String(DECODER.decode(data)));
				String algorithm = objeto.get("algorithm").getAsString();
				String issued_at = objeto.get("issued_at").getAsString();
				validaAssinatura(algorithm, issued_at, assinatura, data);

				// Linux based timestamp in SECONDS
				/*
				 * if (objeto.get("expires") != null) { Long expires =
				 * objeto.get("expires").getAsLong() * 1000; expires = expires -
				 * System.currentTimeMillis();
				 * 
				 * new RevalidateAccessToken().in((int) (expires / 1000)); }
				 */

				if (objeto.get("user_id") != null) {
					String facebookId = objeto.get("user_id").getAsString();
					String oauthToken = objeto.get("oauth_token").getAsString();

					Eleitor eleitor = Eleitor.find("byFacebookID", facebookId).first();
					if (eleitor != null) {
						if (eleitor.getStatus().equals(Status.CONFIGUROU_POLITICOS)) {
							if (!eleitor.getToken().equals(oauthToken)) {
								eleitor.setToken(oauthToken);
							}
							// profile(eleitor); nao funciona nem a pau juvenal
							List<Materia> materias = getMateriasPoliticosAcompanhados(eleitor);
							render("Application/profile.html", eleitor, materias);
						} else if (eleitor.getStatus().equals(Status.CONFIGUROU_LOCALIZACAO)) {
							// escolhePoliticos(eleitor); nao funciona nem a pau
							// juvenal
							List<Casa> casas = getCasasRelevantesParaEleitor(eleitor);
							render("Application/escolhePoliticos.html", eleitor, casas);
						} else if (eleitor.getStatus().equals(Status.NOVO)) {
							render("Application/configuraLocalizacao.html", eleitor);

						}
					} else {
						render("Application/index.html", facebookId, oauthToken);
						// eleitor = inicializaEleitor(facebookId, oauthToken);
						// render("Application/configuraLocalizacao.html",
						// eleitor);
					}
				} else {
					render("Application/redirect.html");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void configuraLocalizacao(String facebookId, String oauthToken) {
		try {
			Eleitor eleitor = inicializaEleitor(facebookId, oauthToken);
			render("Application/configuraLocalizacao.html", eleitor);
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}

	private static void validaAssinatura(String algorithm, String issued_at, String assinatura, String data)
			throws NoSuchAlgorithmException, InvalidKeyException {
		if (!algorithm.equals("HMAC-SHA256")) {
			throw new RuntimeException("Invalid request. (Unsupported algorithm.)");
		}

		if (Long.parseLong(issued_at) < ((System.currentTimeMillis() / 1000) - MAX_AGE)) {
			throw new RuntimeException("Invalid request. (Too old.)");
		}

		byte[] digest = MAC.doFinal(data.getBytes());
		if (!Arrays.equals(DECODER.decode(assinatura), digest)) {
			throw new RuntimeException("Invalid request. (Invalid signature.)");
		}
	}

	public static void escolhePoliticos(Eleitor eleitor) {
		if (eleitor.getId() == null) {
			eleitor = Eleitor.find("byFacebookID", eleitor.getFacebookID()).first();
		} else {
			eleitor.setStatus(Status.CONFIGUROU_LOCALIZACAO);
			eleitor.save();
		}
		List<Casa> casas = getCasasRelevantesParaEleitor(eleitor);
		render(eleitor, casas);
	}

	private static List<Casa> getCasasRelevantesParaEleitor(Eleitor eleitor) {
		List<Casa> casas = Casa
				.find("select casa " + "from Casa casa where "
						+ "(casa.cidade = :cidade AND casa.uf = :uf AND casa.pais = :pais) OR "
						+ "(casa.cidade is null AND casa.uf = :uf AND casa.pais = :pais) OR "
						+ "(casa.cidade is null AND casa.uf is null AND casa.pais = :pais)")
				.bind("cidade", eleitor.getCidade()).bind("uf", eleitor.getUf()).bind("pais", eleitor.getPais())
				.fetch();
		for (Casa casa : casas) {
			if (casa.getCidade() == null) {
				Set<Politico> politicosFinal = new HashSet<Politico>();
				for (Politico politico : casa.getPoliticos()) {
					if (casa.getUf() == null) {
						// senado ou camara
						if (politico.getUf().equals(eleitor.getUf())) {
							politicosFinal.add(politico);
						}
						casa.setPoliticos(politicosFinal);
					} else {
						// camara estadual - do nothing
					}
				}

			} else {
				// camara municipal - do nothing
			}
		}
		return casas;
	}

	private static Eleitor inicializaEleitor(String facebookId, String accessToken) throws ParseException {

		FacebookClient facebookClient = new DefaultFacebookClient(accessToken);
		User u = facebookClient.fetchObject(facebookId, User.class);

		Eleitor eleitor = new Eleitor();
		eleitor.setEmail(u.getEmail());
		eleitor.setFacebookID(facebookId);
		eleitor.setFoto("http://graph.facebook.com/" + facebookId + "/picture");
		eleitor.setDataNascimento(formataData(u.getBirthdayAsDate()));
		eleitor.setGenero(formataGenero(u.getGender()));
		eleitor.setNome(u.getName());
		eleitor.setStatus(Status.NOVO);
		eleitor.setToken(accessToken);
		eleitor.setPais("Brasil");
		eleitor.save();

		return eleitor;
	}

	private static String formataGenero(String generoNaoFormatado) {
		if (generoNaoFormatado == null) {
			return null;
		} else if (MALE.equalsIgnoreCase(generoNaoFormatado)) {
			return "Masculino";
		} else {
			return "Feminino";
		}
	}

	private static String formataData(Date from) throws ParseException {
		return FORMATADOR_PARA.format(from);
	}

	@SuppressWarnings("unused")
	private static void getJson(String uri) throws IOException {
		URL url = new URL(uri);
		URLConnection connection = url.openConnection();
		InputStream input = null;
		input = connection.getInputStream();
		long length = input.available();
		byte[] bytes = new byte[(int) length];
		input.read(bytes);

		ByteArrayOutputStream out = new ByteArrayOutputStream();

		FileOutputStream fos = new FileOutputStream("C:/Users/User/workspace/votovivo/file.mp4");
		fos.write(bytes);
		fos.close();
		input.close();
	}

	@Get("/add")
	public static void add(long eleitorId, long politicoId) {
		Eleitor eleitor = Eleitor.findById(eleitorId);
		Politico politico = Politico.findById(politicoId);

		eleitor.getPoliticos().add(politico);
		eleitor.save();

		renderJSON("{\"resultado\":\"ok\"}");
	}

	@Get("/remove")
	public static void remove(long eleitorId, long politicoId) {
		Eleitor eleitor = Eleitor.findById(eleitorId);
		Politico politico = Politico.findById(politicoId);

		eleitor.getPoliticos().remove(politico);
		eleitor.save();

		renderJSON("{\"resultado\":\"ok\"}");
	}

	@Get("/voto")
	public static void vote(long eleitorId, long materiaId, String voto) {
		Posicao posicao = Posicao.find("from Posicao p where p.materia.id = ? and p.eleitor.id = ?", materiaId,
				eleitorId).first();
		if (posicao == null) {
			posicao = new Posicao();
			Eleitor e = Eleitor.findById(eleitorId);
			Materia m = Materia.findById(materiaId);
			posicao.setEleitor(e);
			posicao.setMateria(m);
		}

		posicao.setVoto(voto);
		posicao.save();

		renderJSON("{\"success\": true, \"voto\":" + voto + "}");
	}

	@Get("/getvotos/{politicoid}")
	public static void getvotos(String politicoid) {
		if (politicoid != null && politicoid.length() > 0) {
			try {
				Politico politico = Politico.find("byVoteNaWebId", politicoid).first();

				Collection<Materia> materias = VoteNaWebParser.load(politico);

				render(materias, politico.getNome());

			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (XPatherException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public static void oauth() {
		redirect("http://apps.facebook.com/votovivo/");
	}
}
